<?php
namespace App\Common\Lib\Cache\KeyDB;

use App\Common as com;
use App\Common\Lib\Cache\KeyDB\_Persistence as namePersistence;
use App\Common\Lib\Cache\KeyDB as nameKeyDB;

/**
 * 数据持久层
 * @property com\Cla\ClaBase\DataStore $dataStore
 */
class _Persistence extends _Base {

	/**
	 * 过期时间
	 * @var int|null [null]永久 [>0]
	 */
	private ?int $expire = null;

	/**
	 * 延迟写入时间
	 * @var int [null]立刻写入 [0]立刻写入(异步，0秒后写入)
	 */
	private ?int $delay = null;

	/**
	 * @return com\Model\KeyDbPersistence\DataManager
	 */
	private function goDirectDataManagerOfRead(  ):com\Model\KeyDbPersistence\DataManager {
	    return $this->goPool( 'goDirectDataManaerOfRead', function(){
	        return $this->newDirectDataManager();
	    } );
	}

	/**
	 * @return com\Model\KeyDbPersistence\DataManager
	 */
	private function newDirectDataManager(  ):com\Model\KeyDbPersistence\DataManager {
	    return com\Model\KeyDbPersistence::dataManager( [
	        'key_name' => $this->getKeyName(),
	    ] );
	}

	/**
	 * @return com\Model\KeyDbPersistence\DataManager
	 */
	private function newDirectDataManagerOfDefault(  ):com\Model\KeyDbPersistence\DataManager {
		$dataUnit = $this->newDirectDataManager();
		$dataUnit->goWhereDy()->valid();
		return $dataUnit;
	}

	/**
	 * 设置持久层过期时间
	 * @param int|null $expire [null]永久 [>0]
	 * @return $this
	 */
	public function expire( ?int $expire ):self {
		$this->expire = $expire;
		//
		return $this;
	}

	/**
	 * 获取持久层过期时间
	 * @return int|null
	 */
	public function getExpire(  ):?int {
		return $this->expire;
	}

	/**
	 * 设置延迟写入时间
	 * @param int|null $delay [null]立刻写入 [0]立刻写入(异步，0秒后写入)
	 * @return $this
	 */
	public function delay( ?int $delay ):self {
		if( $delay < 0 ){
			throw com\Lib\Err\Exception::makeOfNew( 'delay不可小于0', com\Lib\Err\Code::$sysFunParamError );
		}
		$this->delay = $delay;
		//
		return $this;
	}

	/**
	 * 获取延迟写入时间
	 * @return int|null
	 */
	public function getDelay():?int {
		return $this->delay;
	}

	/**
	 * 立即回写缓存数据到持久层
	 * @return bool [true][必返]
	 * @throws
	 */
	public function writeBack ():bool {
		$ttl = $this->dataStore->main->goCache()->ttlOfExpireOrDefault( false );
		if( $ttl === false ){
			// 不存在则视为回写成功
			return true;
		}
		$this->setOfImmediately( $this->dataStore->main->goCache()->get(), $ttl );
		//
		return true;
	}

	/**
	 * 立即设置缓存数据
	 * @param mixed $value 存储数据
	 * @param int|null $expire 有效时间，单位：秒。[null]永久 [int]必须大于0
	 * @return bool [true][必返]
	 * @throws
	 */
	public function setOfImmediately( $value, ?int $expire ):bool {
		if( $expire !== null && $expire <= 0 ){
			throw com\Lib\Err\Exception::makeOfNew( 'expire需大于0', com\Lib\Err\Code::$sysFunParamError );
		}
		$expire = $this->getExpire() ?? $expire;
		$expireTime = $expire === null ? -1 : com\Lib\Fun\Time::getTime() + $expire;
		//
		$this->newDirectDataManager()
			->setData_KeyValue( $value )
			->setData_ExpireTime( $expireTime )
			->updateOrCreate();
		//
		return true;
	}

	/**
	 * 设置缓存数据
	 * @param mixed $value 存储数据
	 * @param int|null $expire 有效时间，单位：秒。[null]永久 [int]必须大于0
	 * @return bool [true][必返]
	 * @throws
	 */
	public function set( $value, ?int $expire ):bool {
		if( $expire !== null && $expire <= 0 ){
			throw com\Lib\Err\Exception::makeOfNew( 'expire需大于0', com\Lib\Err\Code::$sysFunParamError );
		}
		if( $this->getDelay() === null ){
			$this->setOfImmediately( $value, $expire );
		}else{
			nameKeyDB\DelayPool::makeOfStatic()->addPool( $this->getKeyName(), $this->getDelay() );
		}
		//
		return true;
	}

	/**
	 * 删除缓存
	 * @return bool [true][必返]
	 */
	public function del(  ):bool {
		return $this->newDirectDataManager()->deleteTree();
	}

	/**
	 * 获取缓存数据
	 * @param mixed $default 默认值。[false][默认]
	 * @return mixed 数据
	 * @throws
	 */
	public function get( $default = false ) {
		return $this->newDirectDataManagerOfDefault()->getData_KeyValue( $default );
	}

	/**
	 * 判断缓存是否存在
	 * @return boolean 是否存在 [true]存在 [false]不存在
	 */
	public function has(  ):bool {
		return $this->newDirectDataManagerOfDefault()->isExist();
	}

	/**
	 * 设置ttl
	 * @param int|null $expire 有效时间。[null]永久
	 * @return bool
	 */
	public function setTtl( ?int $expire ):bool {
		$res = $this->newDirectDataManager()
			->setData_ExpireTime( com\Lib\Fun\Time::getTime() + $expire )
			->saveOfUpdate();
		return $res > 0;
	}

	/**
	 * 命令以秒为单位返回 key 的剩余过期时间。
	 * @return int 剩余过期时间。[-2]key不存在时 [-1]key 存在但没有设置剩余生存时间 [>=0]剩余过期时间
	 */
	public function ttl():int {
		$expireTime = $this->newDirectDataManager()->getData_ExpireTime( -2 );
		if( $expireTime > 0 ){
			return $expireTime - com\Lib\Fun\Time::getTime();
		}
		//
		return $expireTime;
	}

	/**
	 * 命令以秒为单位返回 key 的剩余过期时间。
	 * @return int|null 剩余过期时间。 [null]永久 [>=0]剩余过期时间
	 */
	public function ttlOfExpireOrFail():?int {
		$ttl = $this->ttl();
		if( $ttl >= 0 ){
			return $ttl;
		}else if( $ttl === -1 ){
			return null;
		}else{
			throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$sysDataError );
		}
	}

	/**
	 * 命令以秒为单位返回 key 的剩余过期时间。
	 * @param  int $default 默认返回过期时间
	 * @return int|null 剩余过期时间。 [null]永久 [>=0]剩余过期时间
	 */
	public function ttlOfExpireOrDefault( int $default ):?int {
		$ttl = $this->ttl();
		if( $ttl >= 0 ){
			return $ttl;
		}else if( $ttl === -1 ){
			return null;
		}else{
			return $default;
		}
	}

	
}
